#include "stdafx.h"
#include "ParticleEmitter.h"
#include "ResourceManager.h"
#include "RenderSystem.h"
#include "ParticleRenderer.h"
#include "Particle.h"
#include "Camera.h"

ParticleEmitter::ParticleEmitter(ResourceManager* resource_manager, RenderSystem* render_system, ParticleRenderer* particle_renderer)
	: m_type(EParticleType::Particle_None), m_resource_manager(resource_manager), m_render_system(render_system), m_particle_renderer(particle_renderer), m_current_time(0.0f)
{
	m_object.m_shader = m_resource_manager->GetShader("particle_vs.hlsl", "particle_ps.hlsl");
	int vs_s[]={sizeof(OncePerFrame), sizeof(OncePerObject)};
	//int ps_s[]={sizeof(DirectionalLight)};
	m_object.m_shader->AllocateVSConstants(2,vs_s);
	//m_shader->AllocatePSConstants(1,ps_s);
	render_system->CreateConstantBuffers(m_object.m_shader);

	Sampler sampler;
	render_system->CreateSamplerState(&sampler,AM_WRAP,AM_WRAP,AM_WRAP,FM_LINEAR,CM_NEVER);
	m_object.m_shader->AllocateSamplers(1);
	m_object.m_shader->SetSampler(0,sampler);

	D3D10_INPUT_ELEMENT_DESC desc[]={
		{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D10_INPUT_PER_VERTEX_DATA,0},
		{"TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,12,D3D10_INPUT_PER_VERTEX_DATA,0}
	};
	render_system->CreateInputLayout(m_object.m_shader,desc,2);
}
ParticleEmitter::~ParticleEmitter(void){}

bool ParticleEmitter::Init(const Vector3& start_position, float emission_rate, unsigned int max_vertices)
{
	m_emission_rate = emission_rate;
	m_position = start_position;

	//m_object.m_shader = m_resource_manager->GetShader("vertexshader.hlsl", "pixelshader.hlsl");
	m_object.m_texture = m_resource_manager->GetTexture("flame.tga");

	m_scene_object = m_particle_renderer->Create(m_object.m_shader, m_object.m_texture, &m_object.m_renderable);

	m_object.m_max_vertices = max_vertices * 4;
	unsigned short* indices = new unsigned short[max_vertices * 6];
	
	for (unsigned int i = 0, index = 0; i < max_vertices * 6; i+=6){
		indices[i+0] = index+0;
		indices[i+1] = index+1;
		indices[i+2] = index+2;
		indices[i+3] = index+2;
		indices[i+4] = index+3;
		indices[i+5] = index+0;
		index += 4;
	}

	m_object.m_renderable.m_count = 0;
	m_render_system->CreateIndexBuffer(&m_object.m_renderable.m_index_buffer, max_vertices * 6, sizeof(unsigned short), indices);
	
	m_object.m_vertices = new ParticleDefines::Vertex[m_object.m_max_vertices];
	m_render_system->CreateVertexBuffer(&m_object.m_renderable.m_vertex_buffer, EAccessMode::BM_DYNAMIC, m_object.m_max_vertices, sizeof(ParticleDefines::Vertex), m_object.m_vertices);

	m_render_system->CreateRasterizerState(&m_rasterizer,CULL_NONE,FILL_SOLID);
	m_scene_object->m_material.m_states.m_rasterizer = &m_rasterizer;

	m_render_system->CreateBlendState(&m_blend_state, EBlendType::BT_SRC_ALPHA, EBlendType::BT_INV_SRC_ALPHA, EBlendType::BT_ZERO, EBlendType::BT_ZERO, EBlendMode::BM_ADD, EBlendMode::BM_ADD);
	m_scene_object->m_material.m_states.m_blend = &m_blend_state;
	
	m_render_system->CreateDepthState(&m_depth_state, true, false);
	m_scene_object->m_material.m_states.m_depth = &m_depth_state;

	return true;
}

void ParticleEmitter::Shut()
{
	delete [] m_object.m_vertices;
}

static void draw_particle(float x, float y, float z, Vector3 up, Vector3 right, float w, float h, ParticleDefines::Vertex* vertices, unsigned int size, unsigned int& pos)
{
	unsigned int i = 0;

	vertices[pos+i].position.x = x + up.x;
	vertices[pos+i].position.y = y + up.y;
	vertices[pos+i].position.z = z + up.z;
	vertices[pos+i].texturecoord.x = 0.0f;
	vertices[pos+i].texturecoord.y = 0.0f;
	i++;

	vertices[pos+i].position.x = (x/*+w*/) + right.x;
	vertices[pos+i].position.y = y  + right.y;
	vertices[pos+i].position.z = z  + right.z;
	vertices[pos+i].texturecoord.x = 1.0f;
	vertices[pos+i].texturecoord.y = 0.0f;
	i++;

	vertices[pos+i].position.x = (x/*+w*/) - up.x;
	vertices[pos+i].position.y = (y/*+h*/) - up.y;
	vertices[pos+i].position.z = z - up.z;
	vertices[pos+i].texturecoord.x = 1.0f;
	vertices[pos+i].texturecoord.y = 1.0f;
	i++;

	vertices[pos+i].position.x = x  - right.x;
	vertices[pos+i].position.y = (y/*+h*/) - right.y;
	vertices[pos+i].position.z = z - right.z;
	vertices[pos+i].texturecoord.x = 0.0f;
	vertices[pos+i].texturecoord.y = 1.0f;
	i++;

	pos += i;
}

void ParticleEmitter::Update(float deltatime, Camera* camera){
	using namespace DirectX;
	m_scene_object->m_transform = DirectX::XMMatrixTranslation(m_position.x, m_position.y, m_position.z);
	m_scene_object->m_bounding_sphere.m_position.x = m_position.x;
	m_scene_object->m_bounding_sphere.m_position.y = m_position.y;
	m_scene_object->m_bounding_sphere.m_position.z = m_position.z;

	m_current_time += deltatime;
	if (m_current_time >= m_emission_rate){
		Vector3 direction(Random::Random(-1.0f, 1.0f),Random::Random(-1.0f, 1.0f),Random::Random(-1.0f, 1.0f));
		direction.Normalize();

		Vector3 position(m_position.x + Random::Random(-1.0f, 1.0f), m_position.y + Random::Random(-1.0f, 1.0f), m_position.z + Random::Random(-1.0f, 1.0f));

		m_particles.push_back(CreateParticle(position, direction, 1.5f));
		m_current_time = 0.0f;
	}
	
	DirectX::XMFLOAT4X4 rot;
	XMStoreFloat4x4(&rot,camera->GetView());
	
	Vector3 up(rot._12, rot._22, rot._32);
	Vector3 right(rot._11, rot._21, rot._31);	

	up.Normalize();
	right.Normalize();

	unsigned int ic = 0;

	for (unsigned int i = 0; i < m_particles.size(); i++){
		if (m_particles[i]->GetLifetime() <= 0.0f){
			delete m_particles[i];
			m_particles.erase(m_particles.begin() + i--);
		}
		else{
			m_particles[i]->Update(deltatime);

			draw_particle(m_particles[i]->GetPosition().x,
				m_particles[i]->GetPosition().y,
				m_particles[i]->GetPosition().z,
				up,
				right,
				1.0f, 1.0f,
				m_object.m_vertices,
				m_object.m_max_vertices,
				ic);
		}
	}

	m_object.m_renderable.m_count = ic/4*6;
	m_render_system->UpdateVertexBuffer(&m_object.m_renderable.m_vertex_buffer,
		m_object.m_max_vertices * sizeof(ParticleDefines::Vertex), m_object.m_vertices);

	//OncePerFrame opf;
	//opf.projection = camera->GetProj();
	//
	//
	//opf.view=XMLoadFloat4x4(&rot);
	//opf.view = DirectX::XMMatrixInverse(nullptr, opf.view);

	//m_object.m_shader->SetVSConstant(0, sizeof(OncePerFrame), &opf);

}

Particle* ParticleEmitter::CreateParticle(const Vector3& start_position, const Vector3& direction, float lifetime){
	return new Particle(start_position, direction, lifetime);
}